package com.ct.commons.aspect.log;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.ct.application.adminlogs.model.po.SysLogSavePO;
import com.ct.application.adminlogs.service.ISysLogService;
import com.ct.application.sysuser.model.vo.SysUserDetailVO;
import com.ct.application.sysuser.request.SysUserRequest;
import com.ct.application.sysuser.service.ISystemUserService;
import com.ct.commons.annotation.ApiLog;
import com.ct.system.orm.AdminSystem;
import com.ct.system.responsity.admin.IAdminSystemUserRepository;
import com.ct.utils.UserAgent;
import com.ct.utils.ipcity.IPLocationFinder;
import com.ct.utils.ipcity.IpAddressUtil;
import com.ct.utils.jwt.UserInfo;
import com.ct.utils.threadlocal.UserThreadLocal;
import eu.bitwalker.useragentutils.Browser;
import eu.bitwalker.useragentutils.OperatingSystem;
import eu.bitwalker.useragentutils.Version;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.Objects;

@Aspect
@Component
@Slf4j
public class LogAspect {

    @Autowired
    private ISysLogService sysLogService;
    @Autowired
    private ISystemUserService systemUserService;
    @Autowired
    private IAdminSystemUserRepository adminSystemUserRepository;
    @Autowired
    private IpAddressUtil ipAddressUtil;
    @Autowired
    private IPLocationFinder locationFinder;

    @Pointcut("@annotation(com.ct.commons.annotation.ApiLog)")
    public void cut() {
    }


    @Around("cut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        // 这里获取请求对象，目的是为了获取：一些关于用户请求信息比如：浏览器，操作系统，ip,
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        // 获取定义ApiLog注解的方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        // 获取方法的签名
        Method method = signature.getMethod();
        // 获取方法是否有@ApiLog注解
        ApiLog methodAnnotation = method.getAnnotation(ApiLog.class);
        // 如果方法存在@ApiLog注解
        if (methodAnnotation != null) {
            // 计时
            long start = System.currentTimeMillis();
            // 执行方法，连接点
            Object result = joinPoint.proceed();
            // 查看耗时
            long time = System.currentTimeMillis() - start;
            log.info("方法：{},执行一共花费了：{} 毫秒", method.getName(), time);
            // 保存系统日志
            saveSystemLog(request, joinPoint, time);
            return result;
        } else {
            // 如果没有加@ApiLog注解的方法就直接放行通过即可。
            Object result = joinPoint.proceed();
            return result;
        }
    }


    private void saveSystemLog(HttpServletRequest request, ProceedingJoinPoint joinPoint, long time) {
        SysLogSavePO sysLogSavePO = new SysLogSavePO();
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        // 获取执行方法
        Method method = methodSignature.getMethod();
        if (Objects.nonNull(method)) {
            // 获取方法注解
            ApiLog apiLog = method.getDeclaredAnnotation(ApiLog.class);
            if (Objects.nonNull(apiLog)) {
                // 获取切入点方法的名字
                String methodName = method.getName();
                //获取方法的参数
                Object[] args = joinPoint.getArgs();
                Object arg = args==null || args.length==0? null: args[0];
                String argStr;
                try {
                    argStr = JSON.toJSONString(arg, SerializerFeature.WriteMapNullValue);
                } catch (Exception e) {
                    argStr = "";
                }
                // 获取切入点所在目标对象
                String className = joinPoint.getTarget().getClass().getName();
                // 获取上下文用户信息
                UserInfo userInfo = UserThreadLocal.get();
                String account = userInfo != null ? userInfo.getAccount() : "";
                Long userId = userInfo != null ? userInfo.getId() : 0L;
                String username = userInfo != null ? userInfo.getUsername() : "";
                String systemName = userInfo != null ? userInfo.getSystemName() : "";
                Long systemId = userInfo != null ? userInfo.getSystemId() : null;
                // 登录成功以后也要拿到用户信息
                if (StringUtils.isNotEmpty(methodName) && methodName.equals("adminLogin")) {
                    SysUserRequest sysUserRequest = (SysUserRequest) args[0];
                    if (Objects.nonNull(sysUserRequest)) {
                        account = sysUserRequest.getAccount();
                        if(StringUtils.isNotEmpty(account)) {
                            SysUserDetailVO sysUserDetailVO = systemUserService.selectByAccount(account);
                            username = sysUserDetailVO.getUsername();
                            if(Objects.isNull(userId) || userId.equals(0L)){
                                userId = sysUserDetailVO.getId();
                            }

                            if(Objects.nonNull(userId)){
                                List<AdminSystem> adminSystemAll = adminSystemUserRepository.findAdminSystemAll(sysUserDetailVO.getId());
                                if(CollectionUtils.isNotEmpty(adminSystemAll)){
                                    userInfo = new UserInfo();
                                    userInfo.setId(sysUserDetailVO.getId());
                                    userInfo.setUsername(sysUserDetailVO.getUsername());
                                    userInfo.setAdmin(sysUserDetailVO.getAdmin());
                                    userInfo.setAccount(sysUserDetailVO.getAccount());
                                    userInfo.setAvatar(sysUserDetailVO.getAvatar());
                                    userInfo.setAppid(adminSystemAll.get(0).getSyscode());
                                    userInfo.setSystemName(adminSystemAll.get(0).getSysname());
                                    userInfo.setSystemId(adminSystemAll.get(0).getId());
                                    UserThreadLocal.set(userInfo);
                                }
                            }
                        }
                    }
                }
                // 根据Ip获取国家，省份，城市和网络
                Map<String, String> ipPossession = ipAddressUtil.getIpPossession(ipAddressUtil.getIpAddress(request));
                // 根据ip获取经纬度
                Map<String, String> stringStringMap = locationFinder.parseIp(ipAddressUtil.getIpAddress(request));

                String userAgent = request.getHeader("User-Agent");
                UserAgent ua = UserAgent.parseUserAgentString(userAgent);
                if (Objects.nonNull(ua)) {
                    Browser browser = ua.getBrowser();
                    Version version = browser != null ? browser.getVersion(userAgent) : null;
                    OperatingSystem os = ua.getOperatingSystem();
                    sysLogSavePO.setBrowser(browser != null ? browser.getName() : "");
                    sysLogSavePO.setBrowserVersion(version != null ? version.getVersion() : "");
                    sysLogSavePO.setOs(os!=null?os.getName():"");
                }else{
                    sysLogSavePO.setBrowser("未知");
                    sysLogSavePO.setBrowserVersion("未知");
                    sysLogSavePO.setOs("未知");
                }
                sysLogSavePO.setSystemId(systemId);
                sysLogSavePO.setSystemName(systemName);
                sysLogSavePO.setClassName(className);
                sysLogSavePO.setMethod(methodName);
                sysLogSavePO.setParams(argStr);
                sysLogSavePO.setRequestIp(ipAddressUtil.getIpAddress(request));
                sysLogSavePO.setLatitude(stringStringMap.get("latitude"));
                sysLogSavePO.setLongitude(stringStringMap.get("longitude"));
                sysLogSavePO.setCountry(ipPossession.get("country"));
                sysLogSavePO.setProvince(ipPossession.get("province"));
                sysLogSavePO.setCity(ipPossession.get("city"));
                sysLogSavePO.setNetwork(ipPossession.get("network"));
                sysLogSavePO.setUserId(userId);
                sysLogSavePO.setAccount(account);
                sysLogSavePO.setUsername(username);
                sysLogSavePO.setAddress(request.getRequestURI());
                sysLogSavePO.setTime(time);
                sysLogSavePO.setDescription(apiLog.value());
                sysLogService.saveUpdateSysLog(sysLogSavePO);
            }
        }
    }
}
